﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.SharePoint.Administration;
using System.Diagnostics;
using Microsoft.SharePoint;
using DVK.SP2007.UniversalClient;
using DVK.SP2007.Constants;

namespace DVK.SP2007
{
    /// <summary>
    /// The DvkMessagesArchiveJob is a timer job. 
    /// It archives messages automatically into DvkArchive list from inbox and outbox list. 
    /// <remarks>
    /// You can specify how old items are moved from inbox/outbox to archive.
    /// The setting in the DvkSettings list is "ArchiveItemsOlderThanInDays" and value must be in days (type int).
    /// If the setting is not specified, the default is 30 days. 
    /// </remarks>
    /// </summary>
    public class DvkMessagesArchiveJob : SPJobDefinition
    {
        private Logger _logger;

        /// <summary>
        /// Initializes a new instance of the <see cref="DvkMessagesArchiveJob"/> class.
        /// </summary>
        public DvkMessagesArchiveJob() : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DvkMessagesArchiveJob"/> class.
        /// </summary>
        /// <param name="jobName">Name of the job.</param>
        /// <param name="service">The service.</param>
        /// <param name="server">The server.</param>
        /// <param name="lockType">Type of the lock.</param>
        public DvkMessagesArchiveJob(string jobName, SPService service, SPServer server, SPJobLockType lockType)
            : base(jobName, service, server, lockType)
        {
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="DvkMessagesArchiveJob"/> class.
        /// </summary>
        /// <param name="jobName">Name of the job.</param>
        /// <param name="webApplication">The web application.</param>
        public DvkMessagesArchiveJob(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.Job)
        {
            
        }

        /// <summary>
        /// Executes the job definition.
        /// 
        /// This starts the archiving.
        /// </summary>
        /// <param name="targetInstanceId">For target types of <see cref="T:Microsoft.SharePoint.Administration.SPContentDatabase"></see> this is the database ID of the content database being processed by the running job. This value is Guid.Empty for all other target types.</param>
        public override void Execute(System.Guid targetInstanceId)
        {
            _logger = JobHelper.GetLoggerForSPJob(LoggingSources.DvkMessagesArchiveJob, this.Properties);
            this._logger.LogInformative("{0}: Logger is logging on {1} level.", LoggingSources.DvkMessagesArchiveJob, _logger.Switch.Level.ToString());
            Stopwatch watcher = new Stopwatch();
            watcher.Start();

            base.Execute(targetInstanceId);

            try
            {
                var siteUrl = this.Properties[Settings.DvkSiteUrl] as string;

                using (SPSite site = new SPSite(siteUrl))
                using (SPWeb web = site.RootWeb)
                {
                    SPFolder targetFolder = MessageArchiver.AddOrGetMonthlyFolder(web);

                    int archiveItemsOlderThanInDays = GetArchiveItemsOlderThanInDays(web);
                    DateTime? lessThanDate = DateTime.Now - TimeSpan.FromDays(archiveItemsOlderThanInDays);

                    ArchiveReceived(web, targetFolder, lessThanDate);
                    ArchiveSent(web, targetFolder, lessThanDate);
                }
            }
            catch (Exception exc)
            {
                _logger.LogError(
                    LoggerEvents.UnhandledExceptionEncountered,
                    exc,
                    "Message archiving was interrupted."
                    );
            }
            finally
            {
                watcher.Stop();
                _logger.LogInformative(
                        "Job last runtime: {0} ms.",
                        watcher.ElapsedMilliseconds
                    );
            }
        }

        private SourceLevels GetLoggingLevel()
        {
            SourceLevels loggingLevel = SourceLevels.Error; //Default
            var jobsLogSourceLevel = this.Properties[Settings.JobsLogSourceLevel] as string;
            if (!string.IsNullOrEmpty(jobsLogSourceLevel))
            {
                if (Enum.GetNames(typeof(SourceLevels)).Contains(jobsLogSourceLevel))
                    loggingLevel = (SourceLevels)Enum.Parse(typeof(SourceLevels), jobsLogSourceLevel);
            }
            return loggingLevel;
        }

        /// <summary>
        /// Archives DVK messages in the DvkOutbox list.
        /// </summary>
        /// <param name="web">The SPWeb.</param>
        /// <param name="targetFolder">The target folder.</param>
        /// <param name="lessThanDate">The less than date.</param>
        private void ArchiveSent(SPWeb web, SPFolder targetFolder, DateTime? lessThanDate)
        {
            //Get Items to move
            SPList sourceList = web.GetListFromUrl(Constants.Urls.ListOutbox);
            SPQuery query = new SPQuery();
            query.RowLimit = 500;
            query.Query =   "<Where>" +
                            "<Lt><FieldRef Name='" + FieldNames.ItemFieldModified + "' /><Value Type='DateTime'>" + XmlHelper.GetISO8601DateTime(lessThanDate) + "</Value></Lt>" +
                            "</Where>";

            SPListItemCollection itemsToMove = sourceList.GetItems(query);
            _logger.LogInformative("{0}: Archiving sent items. Items to move: '{1}'", web.Url, itemsToMove.Count);

            foreach (SPListItem item in itemsToMove)
            {
                try
                {
                    MessageArchiver.ArchiveItem(web, targetFolder, item);
                }
                catch (DvkException exc)
                {
                    _logger.LogError(
                        LoggerEvents.ArchivingMessageFailed,
                        exc,
                        "{0}: Failed to archive sent item with Id: {1}, Name: {2}, Source: {3}, Target: {4}.",
                        web.Url,
                        item.ID,
                        item.Name,
                        item.Url,
                        targetFolder
                        );
                }
            }
        }

        /// <summary>
        /// Archives DVK messages in the DvkInbox list.
        /// </summary>
        /// <param name="web">The SPWeb.</param>
        /// <param name="targetFolder">The target folder.</param>
        /// <param name="lessThanDate">The less than date.</param>
        private void ArchiveReceived(SPWeb web, SPFolder targetFolder, DateTime? lessThanDate)
        {
            //Get Items to move
            SPList sourceList = web.GetListFromUrl(Constants.Urls.ListInbox);
            SPQuery query = new SPQuery();
            query.RowLimit = 500;
            query.Query = "<Where>" +
                            "<Lt><FieldRef Name='" + FieldNames.ItemFieldModified + "' /><Value Type='DateTime'>" + XmlHelper.GetISO8601DateTime(lessThanDate) + "</Value></Lt>" +
                            "</Where>";

            SPListItemCollection itemsToMove = sourceList.GetItems(query);
            _logger.LogInformative("{0}: Archiving received items. Items to move: '{1}'", web.Url, itemsToMove.Count);
            foreach (SPListItem item in itemsToMove)
            {
                try
                {
                    MessageArchiver.ArchiveItem(web, targetFolder, item);
                }
                catch (DvkException exc)
                {
                    _logger.LogError(
                        LoggerEvents.ArchivingMessageFailed,
                        exc,
                        "{0}: Failed to archive received item with Id: {1}, Name: {2}, Source: {3}, Target: {4}.",
                        web.Url,
                        item.ID,
                        item.Name,
                        item.Url,
                        targetFolder
                        );
                }
            }
        }
        /// <summary>
        /// Gets the archive items older than in days.
        /// 
        /// Key in settings: ArchiveItemsOlderThanInDays
        /// 
        /// Default is 30.
        /// </summary>
        /// <param name="settingsManager">The settings manager.</param>
        /// <returns>value in days</returns>
        private int GetArchiveItemsOlderThanInDays(SPWeb web)
        {
            int resultValueInDays;
            string tempValueInDays = new DvkSettingsRepository(web).QuerySetting(Constants.Settings.ArchiveItemsOlderThanInDays);

            if (int.TryParse(tempValueInDays, out resultValueInDays))
            {
                //Return the result of conversion
                return resultValueInDays;
            }

            //Default value is 30.
            return 30;
        }
    }
}
